package com.example.demo.web.redis;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.example.demo.bean.Menu;
import com.example.demo.service.ResinLncrService;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.redisson.api.RLock;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁
 */
@Slf4j
@RestController
@RequestMapping("/redis")
public class RedisController {

    @Resource
    ResinLncrService resinLncrService;

    @Resource
    JdbcTemplate jdbcTemplate;

    @Resource
    StringRedisTemplate stringRedisTemplate;


    @Resource
    RedissonClient redisson;


    @RequestMapping("hello")
    public String hello() {
        RLock lock = redisson.getLock("lock");
        stringRedisTemplate.opsForValue().set("lockss",new DateTime().toString(),1,TimeUnit.DAYS);
        //加锁：默认是30S
        //lock.lock();
        //1 锁的自动续期，如果业务超长，运行期间给锁续上心得30S。不用担心业务时间过长，锁自动过期删掉
        //2 加锁的业务只要运行完成，就不会给当前锁续期，及使不手动解锁，锁默认30S也会自动删除


        //lock.lock(10, TimeUnit.SECONDS); //这个有问题，默认超时时间是指定的时间，不会自动续期
        try {
            System.out.println("加锁成功" + Thread.currentThread().getId());
            TimeUnit.SECONDS.sleep(30);
        } catch (Exception e) {
        } finally {
            lock.unlock();
            System.out.println("释放成功" + Thread.currentThread().getId());
        }
        return Thread.currentThread().getId() + "";
    }


    @RequestMapping("/incr")
    public String incr() {
        resinLncrService.useRedissonForlockWithRedisson();

        System.out.println("被访问ss了9");
        return "OK";
    }

    /******************************
     * 信号量
     */
    @RequestMapping("/tc")
    public Boolean tc() throws Exception {
        return resinLncrService.tc();
    }

    @RequestMapping("/rc")
    public Boolean rc() throws Exception {
        return resinLncrService.rc();
    }

    @RequestMapping("/go")
    public Boolean go() {
        return resinLncrService.gogogo();
    }

    @RequestMapping("/lockdoor")
    public Boolean lockdoor() throws InterruptedException {
        return resinLncrService.lockDoor();

    }

    private String MENU_STR = "key:menu";

    @GetMapping("menu/{id}")
    public Map<String, List<Menu>> getMenuJson(@PathVariable Long id) {
        /**
         * 1 空结果缓存：解决缓存穿透
         * 2 设置过期时间(随机值)：解决缓存雪崩
         * 3 加锁：解决缓存击穿
         */
        /**
         * 本地锁 synchronized
         */
        String s = stringRedisTemplate.opsForValue().get(MENU_STR);
        if (StringUtils.isEmpty(s)) {
            log.info("查询数据库|com.example.demo.web.redis.RedisController.getMenuJson");
            List<Menu> query = getMenus(id);
            //查询后放入redis
            Map<String, List<Menu>> map = new HashMap<>();
            map.put("result", query);
            String jsonString = JSON.toJSONString(map);
            //过期时间
            stringRedisTemplate.opsForValue().set(MENU_STR, jsonString, 1, TimeUnit.DAYS);
            return map;
        }

        // 转为指定对象
        Map<String, List<Menu>> stringListMap = JSON.parseObject(s, new TypeReference<Map<String, List<Menu>>>() {
        });
        return stringListMap;
    }

    /**
     * 切换redis|切换后会使用新的，
     *
     * @param dbIndex
     */
    public void selectDB(int dbIndex) {
        log.info("切换redis DB-{}", dbIndex);
        LettuceConnectionFactory lettuceConnectionFactory = (LettuceConnectionFactory) stringRedisTemplate.getConnectionFactory();
        lettuceConnectionFactory.setDatabase(0);
        lettuceConnectionFactory.setShareNativeConnection(false);
        lettuceConnectionFactory.resetConnection();
        stringRedisTemplate.setConnectionFactory(lettuceConnectionFactory);
    }

    private List<Menu> getMenus(long id) {

        return jdbcTemplate.query("select * from t_menu ", new BeanPropertyRowMapper(Menu.class));
    }


    /**
     * 车库车位 3 车位置
     *
     * @return
     * @throws Exception
     */
    @GetMapping("park")
    public String park() throws Exception {
        selectDB();
        RSemaphore park = redisson.getSemaphore("park");
        park.acquire();//获取一个信号，获取一个值，占一个车位
        return "ok";
    }

    @GetMapping("go")
    public String gogo() throws Exception {
        selectDB();
        RSemaphore park = redisson.getSemaphore("park");
        park.release();//释放一个车位
        return "ok";
    }

    /**
     * 切换数据库
     */
    public void selectDB() {
        selectDB(0);
    }


}
